home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- url.c
-
- This module handles parsing and opening URLs.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <string.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
-
- #include "glob.h"
- #include "url.h"
- #include "article.h"
- #include "strutil.h"
- #include "dialog.h"
- #include "message.h"
- #include "apputil.h"
- #include "fileutil.h"
- #include "newswatcher.h"
- #include "memutil.h"
- #include "resutil.h"
- #include "sfutil.h"
- #include "wind.h"
- #include "full.h"
- #include "subject.h"
- #include "ic.h"
-
-
-
- #define isurlschemechar(c) (isalnum((c)) || c == '+' || c == '.' || c == '-')
-
- #define kOptionClickDlg 162
- #define kCantFindHelperDlgID 153
-
- #define kFetchCreatorType 'FTCh'
- #define kFetchMinVersionNumber 0x02120000
- #define kFetchMinVersionNumberStr "2.1.2"
- #define kFetchMinVersionNumberGURL 0x03000000
-
- #define kAnarchieCreatorType 'Arch'
- #define kAnarchieMinVersionNumber 0x01200000
- #define kAnarchieMinVersionNumberStr "1.2.0"
- #define kAnarchieMinVersionNumberGURL 0x01402003
-
- #define kFTPHelperURLFileType 'AURL'
-
- #define kNetscapeCreatorType 'MOSS'
- #define kNetscapeMinVersionNumber 0x00096003
- #define kNetscapeMinVersionNumberStr "0.93 beta"
-
- #define kMacWebCreatorType 'MWEB'
- #define kMacWebMinVersionNumber 0x00000000
- #define kMacWebMinVersionNumberStr ""
- #define kMacWebMinVersionNumberGURL 0x01004003
-
- #define kMacWAISCreatorType 'MWAS'
- #define kMacWAISMinVersionNumber 0x01298000
- #define kMacWAISMinVersionNumberStr "1.2.9"
-
- #define kMacWebAndWAISOpenURLEventClass 'wwwc'
- #define kMacWebAndWAISOpenURLEventID 'ourl'
- #define kMacWebAndWAISOpenURLKeyword 'kURL'
-
- #define kNCSAMosaicCreatorType 'MOS!'
- #define kNCSAMosaicMinVersionNumber 0x02004008
- #define kNCSAMosaicMinVersionNumberStr "2.0a8"
- #define kNCSAMosaicOpenURLEventClass 'mos!'
- #define kNCSAMosaicOpenURLEventID 'ourl'
-
- #define kTurboGopherCreatorType 'TGOF'
- #define kTurboGopherMinVersionNumber 0x02004001
- #define kTurboGopherMinVersionNumberStr "2.0a1"
- #define kTurboGopherMinVersionNumberCanon 0x02006001
-
- #define kNCSATelnetCreatorType 'NCSA'
- #define kNCSATelnetSettingsFileType 'CONF'
- #define kNCSATelnetMinVersionNumber 0x02400000
- #define kNCSATelnetMinVersionNumberStr "2.4"
- #define kNCSATelnetMinVersionNumberGURL 0x02612004
-
- #define ktn3270CreatorType 'GFTM'
- #define ktn3270SettingsFileType 'GFTS'
- #define ktn3270MinVersionNumber 0x02402007
- #define ktn3270MinVersionNumberStr "2.4d7"
-
- #define kFingerCreatorType 'PnLF'
- #define kFingerMinVersionNumber 0x01500000
- #define kFingerMinVersionNumberStr "1.5.0"
-
- #define kPhCreatorType 'PHED'
- #define kPhMinVersionNumber 0x01200000
- #define kPhMinVersionNumberStr "1.2"
-
-
-
- typedef enum TURLKind {
- kNotURL,
- kMailtoURL,
- kNewsURL,
- kNntpURL,
- kOtherURL
- } TURLKind;
-
- typedef struct THelperInfo {
- OSType sig; /* signature of helper program */
- unsigned long minVersionNumber; /* min version number of helper program */
- char *minVersionStr; /* min version number as a string */
- } THelperInfo;
-
- static THelperInfo gHelperInfo[] = {
- {kAnarchieCreatorType, kAnarchieMinVersionNumber,
- kAnarchieMinVersionNumberStr},
- {kFetchCreatorType, kFetchMinVersionNumber,
- kFetchMinVersionNumberStr},
- {kNetscapeCreatorType, kNetscapeMinVersionNumber,
- kNetscapeMinVersionNumberStr},
- {kMacWebCreatorType, kMacWebMinVersionNumber,
- kMacWebMinVersionNumberStr},
- {kMacWAISCreatorType, kMacWAISMinVersionNumber,
- kMacWAISMinVersionNumberStr},
- {kNCSAMosaicCreatorType, kNCSAMosaicMinVersionNumber,
- kNCSAMosaicMinVersionNumberStr},
- {kTurboGopherCreatorType, kTurboGopherMinVersionNumber,
- kTurboGopherMinVersionNumberStr},
- {kNCSATelnetCreatorType, kNCSATelnetMinVersionNumber,
- kNCSATelnetMinVersionNumberStr},
- {ktn3270CreatorType, ktn3270MinVersionNumber,
- ktn3270MinVersionNumberStr},
- {kFingerCreatorType, kFingerMinVersionNumber,
- kFingerMinVersionNumberStr},
- {kPhCreatorType, kPhMinVersionNumber,
- kPhMinVersionNumberStr},
- {0, 0, nil}
- };
-
- typedef struct TDefaultHelperInfo {
- TURLSchemeName schemeName;
- OSType helpers[5];
- } TDefaultHelperInfo;
-
- static TDefaultHelperInfo gDefaultHelperInfo[] = {
- {"ftp", {kFetchCreatorType, kAnarchieCreatorType, 0, 0, 0}},
- {"http", {kMacWebCreatorType, kNetscapeCreatorType, kNCSAMosaicCreatorType, 0, 0}},
- {"gopher", {kTurboGopherCreatorType, kNetscapeCreatorType, kMacWebCreatorType,
- kNCSAMosaicCreatorType, 0}},
- {"wais", {kMacWAISCreatorType, kNCSAMosaicCreatorType, 0, 0, 0}},
- {"telnet", {kNCSATelnetCreatorType, 0, 0, 0, 0}},
- {"tn3270", {ktn3270CreatorType, 0, 0, 0, 0}},
- {"finger", {kFingerCreatorType, 0, 0, 0, 0}},
- {"whois", {kFingerCreatorType, 0, 0, 0, 0}},
- {"ph", {kPhCreatorType, 0, 0, 0, 0}},
- {"", {0, 0, 0, 0, 0}},
- };
-
-
-
- /*----------------------------------------------------------------------------
- CheckOneDefaultURLHelper
-
- Check to see if we have a usable version of a default URL helper program.
-
- Entry: sig = signature of helper program
-
- Exit: function result = true if we have a usable version of this helper.
- *versionNumber = version number of helper.
- *lastMod = last mod date/time of helper
- ----------------------------------------------------------------------------*/
-
- static Boolean CheckOneDefaultURLHelper (OSType sig, unsigned long *versionNumber,
- unsigned long *lastMod)
- {
- THelperInfo *p;
- OSErr err = noErr;
- FSSpec fSpec;
-
- /* Find the program on the user's disks. */
-
- err = FindAppFromSig(sig, &fSpec, nil, nil);
- if (err != noErr) return false;
-
- /* Find the entry for this program in the gHelperInfo array. */
-
- for (p = gHelperInfo; ; p++) {
- if (p->sig == 0) return false;
- if (p->sig == sig) break;
- }
-
- /* Check the version number. */
-
- if (p->minVersionNumber != 0) {
- err = GetAppVersionNumber(&fSpec, versionNumber);
- if (err != noErr) return false;
- if (*versionNumber < p->minVersionNumber) return false;
- }
-
- /* Get the last mod date and time. */
-
- err = GetLastModDateTime(&fSpec, lastMod);
- if (err != noErr) return false;
-
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- InitDefaultURLHelper
-
- Initialize default URL helper info.
-
- Entry: schemeName = pointer to scheme name.
- ----------------------------------------------------------------------------*/
-
- void InitDefaultURLHelper (TURLSchemeName schemeName)
- {
- short i;
- TURLHelperInfo *p;
- TDefaultHelperInfo *q;
- OSType *helpers, sig;
- unsigned long versionNumber, lastMod;
-
- /* Find the entry for this scheme in the gPrefs.urlHelpers array. If we can't
- find the entry, or if a helper is already set for this scheme, return. */
-
- for (i = 0, p = *gPrefs.urlHelpers; ; i++, p++) {
- if (*p->schemeName == 0) return;
- if (strcmp(schemeName, p->schemeName) == 0) break;
- }
- if (p->sig != 0) return;
-
- /* Search the gDefaultHelperInfo array to find the list of helper sigs
- for this scheme. */
-
- for (q = gDefaultHelperInfo; ; q++) {
- if (*q->schemeName == 0) return;
- if (strcmp(schemeName, q->schemeName) == 0) break;
- }
- helpers = q->helpers;
-
- /* For each sig in the list of helper sigs for this scheme, check to see if
- the user has a copy of the program which is usable as a helper. If we find
- one, set it in the gPrefs.urlHelpers array entry for this scheme. */
-
- while (*helpers != 0) {
- sig = *helpers;
- helpers++;
- if (CheckOneDefaultURLHelper(sig, &versionNumber, &lastMod)) {
- p = &(*gPrefs.urlHelpers)[i];
- p->sig = sig;
- p->versionNumber = versionNumber;
- p->lastMod = lastMod;
- return;
- }
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- ValidURLHelper
-
- Check a file to see if it is a valid URL helper program.
-
- Entry: fSpec = pointer to application file spec.
- helperInfo = pointer to helper info.
-
- Exit: function result = true if valid.
- if the helper is valid:
- helperInfo->sig = signature of helper.
- helperInfo->versionNumber = version number of helper.
- helperInfo->lastMod = last mod date/time of helper.
-
- This function is called when the user selects a new URL helper in the
- preferences dialog. It checks minimum version numbers for the known
- URL helper programs. If the helper is valid, the helper info is filled
- in. If the helper is not valid, an error message is issued.
-
- This function is also recalled whenever the user runs a URL helper
- and the last mod date/time of the helper as recorded in the helper info
- does not match the last mod date/time of the file being executed.
- ----------------------------------------------------------------------------*/
-
- Boolean ValidURLHelper (FSSpec *fSpec, TURLHelperInfo *helperInfo)
- {
- OSErr err = noErr;
- FInfo fInfo;
- OSType sig, sig2;
- unsigned long versionNumber = 0;
- unsigned long minVersionNumber = 0;
- unsigned long lastMod;
- short j;
- CStr255 fmt, msg;
-
- err = FSpGetFInfo(fSpec, &fInfo);
- if (err != noErr) goto exit;
- sig = fInfo.fdCreator;
-
- for (j = 0; ; j++) {
- sig2 = gHelperInfo[j].sig;
- if (sig2 == 0 || sig == sig2) break;
- }
- if (sig2 != 0) minVersionNumber = gHelperInfo[j].minVersionNumber;
-
- if (minVersionNumber != 0) {
- err = GetAppVersionNumber(fSpec, &versionNumber);
- if (err == resNotFound) return false;
- if (err != noErr) goto exit;
- if (versionNumber < minVersionNumber) {
- GetCString(kStrHelperTooOld, fmt);
- p2cstr(fSpec->name);
- sprintf(msg, fmt, fSpec->name, gHelperInfo[j].minVersionStr);
- c2pstr((char*)fSpec->name);
- StopAlertMessage(msg);
- return false;
- }
- }
-
- err = GetLastModDateTime(fSpec, &lastMod);
- if (err != noErr) goto exit;
-
- helperInfo->sig = sig;
- helperInfo->versionNumber = versionNumber;
- helperInfo->lastMod = lastMod;
-
- return true;
-
- exit:
-
- GetCString(kStrUnexpectedErr, fmt);
- sprintf(msg, fmt, err);
- StopAlertMessage(msg);
- return false;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoCantFindHelperDialog
-
- Present a "can't find helper program" dialog.
-
- Entry: msg = error message.
- ----------------------------------------------------------------------------*/
-
- void DoCantFindHelperDialog (char *msg)
- {
- DialogPtr dlg;
- short item;
- OSErr err = noErr;
-
- c2pstr(msg);
- ParamText((StringPtr)msg, "\p", "\p", "\p");
- p2cstr((StringPtr)msg);
- err = MyGetNewDialog(kCantFindHelperDlgID, ok, 0, &dlg);
- SysBeep(0);
- if (err != noErr) return;
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- DoClose(dlg);
- }
-
-
-
- /*----------------------------------------------------------------------------
- HelperErrorMessage
-
- Issue a helper program error message.
-
- Entry: err = error number.
- sig = helper program signature.
-
- Exit: function result = userCanceledErr.
- ----------------------------------------------------------------------------*/
-
- static OSErr HelperErrorMessage (OSErr err, OSType sig)
- {
- CStr255 fmt, msg;
- Str31 name;
-
- *name = 0;
- FindAppNameFromSig(sig, name);
- p2cstr(name);
- if (err == fnfErr) {
- if (*name == 0) {
- GetCString(kStrCantFindURLHelper, msg);
- } else {
- GetCString(kStrURLHelperNotFound, fmt);
- sprintf(msg, fmt, name);
- }
- DoCantFindHelperDialog(msg);
- } else if (err == memFullErr || err == memFragErr || err == appMemFullErr) {
- GetCString(kStrURLHelperNoMem, fmt);
- sprintf(msg, fmt, name);
- ErrorMessage(msg);
- } else {
- GetCString(kStrURLHelperUnexpectedErr, fmt);
- sprintf(msg, fmt, err, name);
- ErrorMessage(msg);
- }
- return userCanceledErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsSlackMailtoURL
-
- Check for a slack emailto URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = true if slack emailto URL.
- url = parsed URL.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsSlackMailtoURL (Handle text, short begin, short end, CStr255 url)
- {
- char *p, *q, *x, *y, *textEnd;
- long len;
- Boolean couldBeEmailAddress = false;
-
- p = *text + begin;
- q = *text + end;
- textEnd = *text + MyGetHandleSize(text);
-
- if (*p == '<') {
- x = p - 1;
- while (x > *text && *x != CR) x--;
- if (x > *text) x++;
- if (MyStrNEqual(x, "From", 4)) {
- y = x+4;
- } else if (MyStrNEqual(x, "Reply-To", 8)) {
- y = x+8;
- } else {
- y = nil;
- }
- if (y != nil) {
- while (isLWSP(*y) && y < p) y++;
- if (y < p && *y == ':') couldBeEmailAddress = true;
- } else {
- y = q + 1;
- while (y < textEnd && isLWSP(*y)) y++;
- if (y + 6 <= textEnd && MyStrNEqual(y, "wrote:", 6))
- couldBeEmailAddress = true;
- }
- if (!couldBeEmailAddress) return false;
- }
-
- x = p;
- while (*x != '@' && x < q) x++;
- if (x >= q) return false;
- while (*x != '.' && x < q) x++;
- if (x >= q) return false;
-
- if (*p == '<') {
- p++;
- q--;
- }
- len = q - p + 1;
- if (len == 0 || len > 255) return false;
- BlockMoveData(p, url, len);
- url[len] = 0;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsSlackNewsURL
-
- Check for a slack news URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = true if slack news URL.
- url = parsed URL.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsSlackNewsURL (Handle text, short begin, short end, CStr255 url)
- {
- char *p, *q, *x;
- long len;
-
- p = *text + begin;
- q = *text + end;
-
- x = p;
- if (*x != '<') return false;
- while (*x != '@' && x < q) x++;
- if (x >= q) return false;
- while (*x != '.' && x < q) x++;
- if (x >= q) return false;
-
- len = q - p + 1;
- if (len == 0 || len > 255) return false;
- BlockMoveData(p, url, len);
- url[len] = 0;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ParseURL
-
- Parse a URL.
-
- Entry: text = handle to text.
- begin = offset in text of beginning of object.
- end = offset in text of end of object.
-
- Exit: function result = kind of URL.
- url = parsed URL.
- *urlBegin = offset in text of beginning of parsed URL.
- *urlEnd = offset in text of end of parsed URL.
- *index = index in gPrefs.urlHelpers array of helper info
- for parsed URL scheme, if function result = kOtherURL.
-
- If begin == end, ParseURL tries to locate the beginning and end of
- the URL.
- ----------------------------------------------------------------------------*/
-
- static TURLKind ParseURL (Handle text, short begin, short end, CStr255 url,
- short *urlBegin, short *urlEnd, short *index)
- {
- char *textEnd, *p, *q, *x, *y, *a, *b;
- long len;
- TURLKind urlKind;
- Boolean isMultiLineURL = false, mightBeMultiLineURL = false;
- short numCR;
- TURLHelperInfo *helperInfo;
- short i;
- TURLSchemeName schemeName;
-
- p = *text + begin;
- q = *text + end - 1;
-
- if (begin == end) {
-
- textEnd = *text + MyGetHandleSize(text);
-
- numCR = 0;
- x = p;
- y = q;
- while (true) {
- while (x >= *text && *x != '<' && *x != '>' &&
- *x != '"' && *x != CR) x--;
- if (x < *text || *x == '>' || *x == '"') break;
- if (*x == CR) {
- numCR++;
- if (numCR > 10) break;
- x--;
- } else { /* *x == '<' */
- a = x+1;
- while (a < textEnd && isurlschemechar(*a)) a++;
- mightBeMultiLineURL = a < textEnd && *a == ':';
- break;
- }
- }
- if (mightBeMultiLineURL) {
- numCR = 0;
- while (true) {
- while (y < textEnd && *y != '<' && *y != '>' &&
- *y != '"' && *y != CR) y++;
- if (y >= textEnd || *y == '<' || *y == '"') break;
- if (*y == CR) {
- numCR++;
- if (numCR > 10) break;
- y++;
- } else { /* *y == '>' */
- isMultiLineURL = true;
- break;
- }
- }
- }
-
- if (isMultiLineURL) {
- p = x;
- q = y;
- } else {
- while (p >= *text && !isLWSPorCR(*p) && *p != '<' && *p != '"') p--;
- if (*p != '<') p++;
- while (q < textEnd && !isLWSPorCR(*q) && *q != '>' && *q != '"') q++;
- if (*q != '>') q--;
- }
-
- if (p >= q) return kNotURL;
-
- }
-
- while (p < q && isLWSPorCR(*p)) p++;
- while (p < q && isLWSPorCR(*q)) q--;
-
- if (*p == '<') {
- if (*q != '>') return kNotURL;
- } else {
- if (*q == '>') return kNotURL;
- }
-
- *urlBegin = p - *text;
- *urlEnd = q - *text + 1;
- x = p;
- if (*x == '<') x++;
- y = x;
- while (isurlschemechar(*y) && y < q) y++;
- if (*y == ':') {
- if (MyStrNEqual(x, "url:", 4)) x += 4;
- if (*q == '>') q--;
- len = q - x + 1;
- if (len == 0 || len > 255) return kNotURL;
- BlockMoveData(x, url, len);
- url[len] = 0;
- b = url;
- while (*b != ':' && *b != 0) b++;
- if (*b == ':') {
- for (a = url; a < b; a++) *a = tolower(*a);
- }
- if (MyStrNEqual(url, "mailto", 6)) {
- urlKind = kMailtoURL;
- goto exit;
- }
- if (MyStrNEqual(url, "news", 4)) {
- urlKind = kNewsURL;
- goto exit;
- }
- if (MyStrNEqual(url, "nntp", 4)) {
- urlKind = kNntpURL;
- goto exit;
- }
- for (a = url, b = schemeName;
- *a != 0 && *a != ':' && b < schemeName + kMaxSchemeNameLen;
- a++, b++) *b = *a;
- *b = 0;
- MyICReadURLHelperPref(schemeName);
- for (i = 0, helperInfo = *gPrefs.urlHelpers;
- *helperInfo->schemeName != 0;
- i++, helperInfo++)
- {
- if (MyStrEqual(schemeName, helperInfo->schemeName)) {
- urlKind = kOtherURL;
- *index = i;
- goto exit;
- }
- }
- }
-
- begin = p - *text;
- end = q - *text;
- if (IsSlackMailtoURL(text, begin, end, url)) {
- urlKind = kMailtoURL;
- goto exit;
- }
- if (IsSlackNewsURL(text, begin, end, url)) {
- urlKind = kNewsURL;
- goto exit;
- }
- return kNotURL;
-
- exit:
-
- /* Strip line breaks and white space around line breaks from the url. */
-
- for (p = q = url; *p != 0; p++) {
- if (*p == CR) {
- q--;
- while (q >= url && isLWSP(*q)) q--;
- q++;
- p++;
- while (*p != 0 && isLWSP(*p)) p++;
- p--;
- } else {
- *q++ = *p;
- }
- }
- *q = 0;
- return urlKind;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ParseNntpURL
-
- Parse an nntp URL.
-
- Entry: url = URL string.
-
- Exit: function result = error code (paramErr if syntax error).
- host = news server host address.
- *port = port number.
- newsgroup = newsgroup name.
- *artNumber = article number.
- ----------------------------------------------------------------------------*/
-
- static OSErr ParseNntpURL (char *url, char *host, short *port,
- char *newsgroup, long *artNumber)
- {
- char *p, *q;
-
- p = url + strlen("nntp://");
-
- q = host;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
-
- if (*p == ':') {
- *port = atoi(p+1);
- while (*p != '/' && *p != 0) p++;
- } else {
- *port = kNNTPPort;
- }
-
- if (*p != '/') return paramErr;
- p++;
- q = newsgroup;
- while (*p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
-
- if (*p != '/') return paramErr;
- *artNumber = atol(p+1);
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateAURLFile
-
- Create Fetch or Anarchie AURL file.
-
- Entry: sig = signature of helper.
- url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created AURL file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateAURLFile (OSType sig, char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0, len;
- char cmd[1000];
- long count;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature, sig, kFTPHelperURLFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
- len = strlen(url);
- sprintf(cmd, "FCH %s", url);
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateNCSATelnetSettingsFile
-
- Create an NCSA Telnet settings file.
-
- Entry: url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created settings file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateNCSATelnetSettingsFile (char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0;
- char cmd[1000];
- long count;
- CStr255 hostName;
- short port;
- char *p, *q;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature,
- kNCSATelnetCreatorType, kNCSATelnetSettingsFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
-
- p = url + strlen("telnet://");
- q = p;
- while (*q != '@' && *q != 0) q++;
- if (*q == '@') p = q+1;
- q = hostName;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
- if (*p == ':') {
- port = atoi(p+1);
- } else {
- port = 23;
- }
- sprintf(cmd, "name=\"%s\"\rhost=\"%s\"\rport=\"%d\"\r", url, hostName, port);
-
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CreateTn3270SettingsFile
-
- Create a tn3270 settings file.
-
- Entry: url = URL string.
-
- Exit: function result = error code.
- *docSpec = file spec of created settings file.
- ----------------------------------------------------------------------------*/
-
- static OSErr CreateTn3270SettingsFile (char *url, FSSpec *docSpec)
- {
- OSErr err = noErr;
- short fRefNum = 0;
- char cmd[1000];
- long count;
- CStr255 hostName;
- char *p, *q;
-
- err = CreateTemporaryFile(docSpec, kNewsWatcherSignature,
- ktn3270CreatorType, ktn3270SettingsFileType);
- if (err != noErr) return err;
- err = FSpOpenDF(docSpec, fsRdWrPerm, &fRefNum);
- if (err != noErr) return err;
-
- p = url + strlen("tn3270://");
- q = p;
- while (*q != '@' && *q != 0) q++;
- if (*q == '@') p = q+1;
- q = hostName;
- while (*p != ':' && *p != '/' && *p != 0) *q++ = *p++;
- *q = 0;
- sprintf(cmd, "host_name=\"%s\"\rwindow_title=\"%s\"\r", hostName, url);
-
- count = strlen(cmd);
- err = MyFSWriteNoCache(fRefNum, &count, cmd, nil);
- MyFSClose(fRefNum, nil);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenHelperWithURL
-
- Open a helper program and pass it a URL string.
-
- Entry: helperInfo = pointer to helper info.
- url = URL string.
- foreground = true to run helper in foreground, false to run it
- in the background.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenHelperWithURL (TURLHelperInfo *helperInfo, CStr255 url,
- Boolean foreground)
- {
- OSErr err = noErr;
- ProcessSerialNumber psn;
- FSSpec appSpec, docSpec;
- Boolean running;
- unsigned short launchControlFlags;
- char hackedURL[500];
- unsigned long lastMod;
-
- launchControlFlags = launchContinue | launchNoFileFlags | launchUseMinimum;
- if (!foreground) launchControlFlags |= launchDontSwitch;
-
- err = FindAppFromSig(helperInfo->sig, &appSpec, &running, &psn);
- if (err != noErr) goto exit;
-
- err = GetLastModDateTime(&appSpec, &lastMod);
- if (err != noErr) goto exit;
- if (lastMod != helperInfo->lastMod) {
- if (!ValidURLHelper(&appSpec, helperInfo)) return userCanceledErr;
- }
-
- switch (helperInfo->sig) {
-
- case kFetchCreatorType:
-
- if (helperInfo->versionNumber >= kFetchMinVersionNumberGURL) {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- } else {
- err = CreateAURLFile(helperInfo->sig, url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- }
- break;
-
- case kAnarchieCreatorType:
-
- if (helperInfo->versionNumber >= kAnarchieMinVersionNumberGURL) {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- } else {
- err = CreateAURLFile(helperInfo->sig, url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- }
- break;
-
- case kMacWebCreatorType:
-
- if (helperInfo->versionNumber >= kMacWebMinVersionNumberGURL) {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- } else {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kMacWebAndWAISOpenURLEventClass, kMacWebAndWAISOpenURLEventID,
- kMacWebAndWAISOpenURLKeyword, url, 0, launchControlFlags);
- }
- break;
-
- case kMacWAISCreatorType:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kMacWebAndWAISOpenURLEventClass, kMacWebAndWAISOpenURLEventID,
- kMacWebAndWAISOpenURLKeyword, url, 0, launchControlFlags);
- break;
-
- case kNCSAMosaicCreatorType:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kNCSAMosaicOpenURLEventClass, kNCSAMosaicOpenURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- break;
-
- case kTurboGopherCreatorType:
-
- if (helperInfo->versionNumber < kTurboGopherMinVersionNumberCanon) {
- /* Special case for version 2.0a1 - must add <URL:...> wrapper. */
- if (MyStrNEqual(url, "<URL:", 5)) {
- strcpy(hackedURL, url);
- } else if (MyStrNEqual(url, "URL:", 4)) {
- sprintf(hackedURL, "<%s>", url);
- } else {
- sprintf(hackedURL, "<URL:%s>", url);
- }
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, hackedURL, 0, launchControlFlags);
- } else {
- /* Versions >= 2.0b1 accept canoncial form without wrapper. */
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- }
- break;
-
- case kNCSATelnetCreatorType:
-
- if (helperInfo->versionNumber >= kNCSATelnetMinVersionNumberGURL) {
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- } else {
- err = CreateNCSATelnetSettingsFile(url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- }
- break;
-
- case ktn3270CreatorType:
-
- err = CreateTn3270SettingsFile(url, &docSpec);
- if (err != noErr) goto exit;
- err = LaunchAppWithDoc(running, &appSpec, &psn, &docSpec, 0,
- launchControlFlags);
- break;
-
- default:
-
- err = LaunchAppWithEventAndString(running, &appSpec, &psn,
- kGetURLEventClass, kGetURLEventID,
- keyDirectObject, url, 0, launchControlFlags);
- break;
-
- }
-
- if (err != noErr) goto exit;
-
- return noErr;
-
- exit:
-
- return HelperErrorMessage(err, helperInfo->sig);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FlashObject
-
- Flash an object in an article, message, or text window.
-
- Entry: theTE = handle to TextEdit record.
- start = starting position of object.
- end = ending position of object.
- ----------------------------------------------------------------------------*/
-
- static void FlashObject (TEHandle theTE, short start, short end)
- {
- short i;
- long finalTick;
-
- TESetSelect(start, end, theTE);
- for (i = 0; i < 2; i++) {
- Delay(5, &finalTick);
- TEDeactivate(theTE);
- Delay(5, &finalTick);
- TEActivate(theTE);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- ProcessURLEscapeCodes
-
- Process "%xx" escape codes in a URL string (replace them by the characters
- they represent).
-
- Entry: url = URL with escape codes.
-
- Exit: url = URL with escape codes replaced by the characters they
- represent.
- ----------------------------------------------------------------------------*/
-
- static void ProcessURLEscapeCodes (char *url)
- {
- char *p, *q;
- char c1, c2;
-
- p = q = url;
- while (*p != 0) {
- if (*p == '%') {
- c1 = tolower(*(p+1));
- c2 = tolower(*(p+2));
- if (isxdigit(c1) && isxdigit(c2)) {
- c1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
- c2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
- *q++ = (c1 << 4) + c2;
- p += 3;
- } else {
- *q++ = *p++;
- }
- } else {
- *q++ = *p++;
- }
- }
- *q = 0;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenNewsURL
-
- Open a news URL or slack URL.
-
- Entry: url = news URL or slack URL.
-
- Exit: function result = error code (fnfErr if article or group not found).
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenNewsURL (char *url)
- {
- char *p;
- Cell theCell;
- Boolean hasArts;
-
- if (MyStrNEqual(url, "news:", 5)) {
- if (url[5] == '*' && url[6] == 0) {
- /* Show full group list window */
- if (((WindowPeek)gFullGroupWindow)->visible) {
- MySelectWindow(gFullGroupWindow);
- return noErr;
- } else {
- return DoShowHideFullGroupList();
- }
- } else {
- for (p = url; *p != 0; p++)
- if (*p == '@')
- return OpenReferencedArticle(url);
- /* Open group from full group list */
- theCell.h = 0;
- theCell.v = FindGroupIndex(url+5);
- if (theCell.v == -1) return fnfErr;
- return OpenGroupCell(gFullGroupWindow, theCell, gPrefs.maxFetch, &hasArts);
- }
- } else {
- return OpenReferencedArticle(url);
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenURL
-
- Open a URL.
-
- Entry: wind = pointer to article, message, or text window.
- theTE = handle to text edit record.
- begin = offset in text of beginning of url.
- end = offset in text of end of url.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr OpenURL (WindowPtr wind, TEHandle theTE, short begin, short end)
- {
- TWindow **info;
- CStr255 url, msg;
- short urlBegin, urlEnd;
- TURLKind urlKind;
- TURLHelperInfo helperInfo;
- Boolean foreground, isFTP;
- CStr255 host, newsgroup;
- short port, index, len;
- long artNumber;
- OSErr err = noErr;
- TURLSchemeName schemeName;
- short i;
- TURLHelperInfo *p;
-
- info = (TWindow**)GetWRefCon(wind);
- urlKind = ParseURL((**theTE).hText, begin, end, url, &urlBegin, &urlEnd, &index);
- if (urlKind == kNotURL) {
- SysBeep(0);
- return userCanceledErr;
- }
- FlashObject(theTE, urlBegin, urlEnd);
-
- switch (urlKind) {
-
- case kMailtoURL:
-
- ProcessURLEscapeCodes(url);
- return OpenMailWindow(url);
-
- case kNewsURL:
-
- ProcessURLEscapeCodes(url);
- err = OpenNewsURL(url);
- if (err == fnfErr) {
- NoteMessageNumber(kStrArticleOrGroupNotFound);
- return userCanceledErr;
- } else {
- return err;
- }
-
- case kNntpURL:
-
- ProcessURLEscapeCodes(url);
- err = ParseNntpURL(url, host, &port, newsgroup, &artNumber);
- if (err == paramErr) {
- ErrorMessageNumber(kStrNntpURLSyntaxError);
- return userCanceledErr;
- } else if (err != noErr) {
- return err;
- }
- err = OpenArticleOnAnyServer(host, port, newsgroup, artNumber);
- if (err == fnfErr) {
- NoteMessageNumber(kStrArticleOrGroupNotFound);
- return userCanceledErr;
- } else {
- return err;
- }
-
- case kOtherURL:
-
- strcpy(schemeName, (*gPrefs.urlHelpers)[index].schemeName);
- isFTP = MyStrEqual(schemeName, "ftp");
- if (isFTP && gPrefs.useWebHelperForHtmlFiles) {
- len = strlen(url);
- if ((len > 5 && MyStrNEqual(url + len - 5, ".html", 5)) ||
- (len > 4 && MyStrNEqual(url + len - 4, ".htm", 4)))
- {
- for (i = 0, p = *gPrefs.urlHelpers; *p->schemeName != 0; i++, p++)
- {
- if (MyStrEqual("http", p->schemeName)) {
- index = i;
- strcpy(schemeName, "http");
- isFTP = false;
- break;
- }
- }
- }
- }
- InitDefaultURLHelper(schemeName);
- helperInfo = (*gPrefs.urlHelpers)[index];
- foreground = !isFTP || url[strlen(url)-1] == '/';
- if (helperInfo.sig != 0) {
- err = OpenHelperWithURL(&helperInfo, url, foreground);
- (*gPrefs.urlHelpers)[index] = helperInfo;
- return err;
- } else {
- GetCString(kStrCantFindDefaultURLHelper, msg);
- DoCantFindHelperDialog(msg);
- return userCanceledErr;
- }
-
- }
-
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CommandClick
-
- Handle a command-click in an article, message, or text window.
-
- Entry: wind = pointer to article, message, or text window.
- theTE = handle to TextEdit record in which command-click occurred.
- oldSelStart = start of selection range before user command-clicked.
- oldSelEnd = end of selection range before user command-clicked.
- modifiers = modifiers field from event record.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr CommandClick (WindowPtr wind, TEHandle theTE,
- short oldSelStart, short oldSelEnd, short modifiers)
- {
- TWindow **info;
- short selStart, selEnd, begin, end;
- DialogPtr dlg;
- short item;
- OSErr err = noErr;
-
- if (!gStartupOK) return noErr;
-
- if ((modifiers & optionKey) == 0 && (modifiers & cmdKey) == 0) return noErr;
-
- if ((modifiers & cmdKey) == 0) {
- err = MyGetNewDialog(kOptionClickDlg, ok, cancel, &dlg);
- SysBeep(0);
- if (err != noErr) return err;
- MyModalDialog(dlg, gDialogFilterUPP, &item);
- DoClose(dlg);
- if (item == cancel) return userCanceledErr;
- }
-
- info = (TWindow**)GetWRefCon(wind);
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- if (selStart != selEnd) return noErr;
-
- if (oldSelStart < oldSelEnd && oldSelStart <= selStart &&
- selStart <= oldSelEnd)
- {
- begin = oldSelStart;
- end = oldSelEnd;
- } else {
- begin = end = selStart;
- }
-
- return OpenURL(wind, theTE, begin, end);
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoOpenURL
-
- Handle the "Open URL" command.
-
- Entry: wind = pointer to article, message, or text window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr DoOpenURL (WindowPtr wind)
- {
- TWindow **info;
- TEHandle theTE;
- TMsgFieldInfo **fields;
- short curField;
-
- info = (TWindow**)GetWRefCon(wind);
- if ((**info).kind == kMessage) {
- fields = (**info).fields;
- curField = (**info).curField;
- theTE = (*fields)[curField].edit;
- } else {
- theTE = (**info).theTE;
- }
- return OpenURL(wind, theTE, (**theTE).selStart, (**theTE).selEnd);
- }
-
-
-
- /*----------------------------------------------------------------------------
- OpenURLString
-
- Open a news, nntp, or mailto URL.
-
- Entry: urlString = the URL string.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr OpenURLString (char *urlString)
- {
- OSErr err = noErr;
- Handle h = nil;
- short len, urlBegin, urlEnd;
- CStr255 url;
- TURLKind urlKind;
- CStr255 host, newsgroup;
- short port;
- long artNumber;
- short index;
-
- len = strlen(urlString);
- err = MyPtrToHand(urlString, &h, len);
- if (err != noErr) return err;
- urlKind = ParseURL(h, 0, len, url, &urlBegin, &urlEnd, &index);
- MyDisposeHandle(h);
- ProcessURLEscapeCodes(url);
-
- switch (urlKind) {
-
- case kMailtoURL:
-
- return OpenMailWindow(url);
-
- case kNewsURL:
-
- return OpenNewsURL(url);
-
- case kNntpURL:
-
- err = ParseNntpURL(url, host, &port, newsgroup, &artNumber);
- if (err != noErr) return err;
- return OpenArticleOnAnyServer(host, port, newsgroup, artNumber);
-
- default:
-
- return paramErr;
-
- }
- }
-